1   /*
2    * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package javax.security.auth;
27  
28  import java.util.*;
29  import java.text.MessageFormat;
30  import java.security.Permission;
31  import java.security.PermissionCollection;
32  import java.security.Principal;
33  import sun.security.util.ResourcesMgr;
34  
35  /**
36   * This class is used to protect access to private Credentials
37   * belonging to a particular <code>Subject</code>.  The <code>Subject</code>
38   * is represented by a Set of Principals.
39   *
40   * <p> The target name of this <code>Permission</code> specifies
41   * a Credential class name, and a Set of Principals.
42   * The only valid value for this Permission's actions is, "read".
43   * The target name must abide by the following syntax:
44   *
45   * <pre>
46   *      CredentialClass {PrincipalClass "PrincipalName"}*
47   * </pre>
48   *
49   * For example, the following permission grants access to the
50   * com.sun.PrivateCredential owned by Subjects which have
51   * a com.sun.Principal with the name, "duke".  Note that although
52   * this example, as well as all the examples below, do not contain
53   * Codebase, SignedBy, or Principal information in the grant statement
54   * (for simplicity reasons), actual policy configurations should
55   * specify that information when appropriate.
56   *
57   * <pre>
58   *
59   *    grant {
60   *      permission javax.security.auth.PrivateCredentialPermission
61   *              "com.sun.PrivateCredential com.sun.Principal \"duke\"",
62   *              "read";
63   *    };
64   * </pre>
65   *
66   * If CredentialClass is "*", then access is granted to
67   * all private Credentials belonging to the specified
68   * <code>Subject</code>.
69   * If "PrincipalName" is "*", then access is granted to the
70   * specified Credential owned by any <code>Subject</code> that has the
71   * specified <code>Principal</code> (the actual PrincipalName doesn't matter).
72   * For example, the following grants access to the
73   * a.b.Credential owned by any <code>Subject</code> that has
74   * an a.b.Principal.
75   *
76   * <pre>
77   *    grant {
78   *      permission javax.security.auth.PrivateCredentialPermission
79   *              "a.b.Credential a.b.Principal "*"",
80   *              "read";
81   *    };
82   * </pre>
83   *
84   * If both the PrincipalClass and "PrincipalName" are "*",
85   * then access is granted to the specified Credential owned by
86   * any <code>Subject</code>.
87   *
88   * <p> In addition, the PrincipalClass/PrincipalName pairing may be repeated:
89   *
90   * <pre>
91   *    grant {
92   *      permission javax.security.auth.PrivateCredentialPermission
93   *              "a.b.Credential a.b.Principal "duke" c.d.Principal "dukette"",
94   *              "read";
95   *    };
96   * </pre>
97   *
98   * The above grants access to the private Credential, "a.b.Credential",
99   * belonging to a <code>Subject</code> with at least two associated Principals:
100  * "a.b.Principal" with the name, "duke", and "c.d.Principal", with the name,
101  * "dukette".
102  *
103  */
104 public final class PrivateCredentialPermission extends Permission {
105 
106     private static final long serialVersionUID = 5284372143517237068L;
107 
108     private static final CredOwner[] EMPTY_PRINCIPALS = new CredOwner[0];
109 
110     /**
111      * @serial
112      */
113     private String credentialClass;
114 
115     /**
116      * @serial The Principals associated with this permission.
117      *          The set contains elements of type,
118      *          <code>PrivateCredentialPermission.CredOwner</code>.
119      */
120     private Set principals;  // ignored - kept around for compatibility
121     private transient CredOwner[] credOwners;
122 
123     /**
124      * @serial
125      */
126     private boolean testing = false;
127 
128     /**
129      * Create a new <code>PrivateCredentialPermission</code>
130      * with the specified <code>credentialClass</code> and Principals.
131      */
132     PrivateCredentialPermission(String credentialClass,
133                         Set<Principal> principals) {
134 
135         super(credentialClass);
136         this.credentialClass = credentialClass;
137 
138         synchronized(principals) {
139             if (principals.size() == 0) {
140                 this.credOwners = EMPTY_PRINCIPALS;
141             } else {
142                 this.credOwners = new CredOwner[principals.size()];
143                 int index = 0;
144                 Iterator<Principal> i = principals.iterator();
145                 while (i.hasNext()) {
146                     Principal p = i.next();
147                     this.credOwners[index++] = new CredOwner
148                                                 (p.getClass().getName(),
149                                                 p.getName());
150                 }
151             }
152         }
153     }
154 
155     /**
156      * Creates a new <code>PrivateCredentialPermission</code>
157      * with the specified <code>name</code>.  The <code>name</code>
158      * specifies both a Credential class and a <code>Principal</code> Set.
159      *
160      * <p>
161      *
162      * @param name the name specifying the Credential class and
163      *          <code>Principal</code> Set. <p>
164      *
165      * @param actions the actions specifying that the Credential can be read.
166      *
167      * @throws IllegalArgumentException if <code>name</code> does not conform
168      *          to the correct syntax or if <code>actions</code> is not "read".
169      */
170     public PrivateCredentialPermission(String name, String actions) {
171         super(name);
172 
173         if (!"read".equalsIgnoreCase(actions))
174             throw new IllegalArgumentException
175                 (ResourcesMgr.getString("actions.can.only.be.read."));
176         init(name);
177     }
178 
179     /**
180      * Returns the Class name of the Credential associated with this
181      * <code>PrivateCredentialPermission</code>.
182      *
183      * <p>
184      *
185      * @return the Class name of the Credential associated with this
186      *          <code>PrivateCredentialPermission</code>.
187      */
188     public String getCredentialClass() {
189         return credentialClass;
190     }
191 
192     /**
193      * Returns the <code>Principal</code> classes and names
194      * associated with this <code>PrivateCredentialPermission</code>.
195      * The information is returned as a two-dimensional array (array[x][y]).
196      * The 'x' value corresponds to the number of <code>Principal</code>
197      * class and name pairs.  When (y==0), it corresponds to
198      * the <code>Principal</code> class value, and when (y==1),
199      * it corresponds to the <code>Principal</code> name value.
200      * For example, array[0][0] corresponds to the class name of
201      * the first <code>Principal</code> in the array.  array[0][1]
202      * corresponds to the <code>Principal</code> name of the
203      * first <code>Principal</code> in the array.
204      *
205      * <p>
206      *
207      * @return the <code>Principal</code> class and names associated
208      *          with this <code>PrivateCredentialPermission</code>.
209      */
210     public String[][] getPrincipals() {
211 
212         if (credOwners == null || credOwners.length == 0) {
213             return new String[0][0];
214         }
215 
216         String[][] pArray = new String[credOwners.length][2];
217         for (int i = 0; i < credOwners.length; i++) {
218             pArray[i][0] = credOwners[i].principalClass;
219             pArray[i][1] = credOwners[i].principalName;
220         }
221         return pArray;
222     }
223 
224     /**
225      * Checks if this <code>PrivateCredentialPermission</code> implies
226      * the specified <code>Permission</code>.
227      *
228      * <p>
229      *
230      * This method returns true if:
231      * <p><ul>
232      * <li> <i>p</i> is an instanceof PrivateCredentialPermission and <p>
233      * <li> the target name for <i>p</i> is implied by this object's
234      *          target name.  For example:
235      * <pre>
236      *  [* P1 "duke"] implies [a.b.Credential P1 "duke"].
237      *  [C1 P1 "duke"] implies [C1 P1 "duke" P2 "dukette"].
238      *  [C1 P2 "dukette"] implies [C1 P1 "duke" P2 "dukette"].
239      * </pre>
240      * </ul>
241      *
242      * <p>
243      *
244      * @param p the <code>Permission</code> to check against.
245      *
246      * @return true if this <code>PrivateCredentialPermission</code> implies
247      * the specified <code>Permission</code>, false if not.
248      */
249     public boolean implies(Permission p) {
250 
251         if (p == null || !(p instanceof PrivateCredentialPermission))
252             return false;
253 
254         PrivateCredentialPermission that = (PrivateCredentialPermission)p;
255 
256         if (!impliesCredentialClass(credentialClass, that.credentialClass))
257             return false;
258 
259         return impliesPrincipalSet(credOwners, that.credOwners);
260     }
261 
262     /**
263      * Checks two <code>PrivateCredentialPermission</code> objects for
264      * equality.  Checks that <i>obj</i> is a
265      * <code>PrivateCredentialPermission</code>,
266      * and has the same credential class as this object,
267      * as well as the same Principals as this object.
268      * The order of the Principals in the respective Permission's
269      * target names is not relevant.
270      *
271      * <p>
272      *
273      * @param obj the object we are testing for equality with this object.
274      *
275      * @return true if obj is a <code>PrivateCredentialPermission</code>,
276      *          has the same credential class as this object,
277      *          and has the same Principals as this object.
278      */
279     public boolean equals(Object obj) {
280         if (obj == this)
281             return true;
282 
283         if (! (obj instanceof PrivateCredentialPermission))
284             return false;
285 
286         PrivateCredentialPermission that = (PrivateCredentialPermission)obj;
287 
288         return (this.implies(that) && that.implies(this));
289     }
290 
291     /**
292      * Returns the hash code value for this object.
293      *
294      * @return a hash code value for this object.
295      */
296     public int hashCode() {
297         return this.credentialClass.hashCode();
298     }
299 
300     /**
301      * Returns the "canonical string representation" of the actions.
302      * This method always returns the String, "read".
303      *
304      * <p>
305      *
306      * @return the actions (always returns "read").
307      */
308     public String getActions() {
309         return "read";
310     }
311 
312     /**
313      * Return a homogeneous collection of PrivateCredentialPermissions
314      * in a <code>PermissionCollection</code>.
315      * No such <code>PermissionCollection</code> is defined,
316      * so this method always returns <code>null</code>.
317      *
318      * <p>
319      *
320      * @return null in all cases.
321      */
322     public PermissionCollection newPermissionCollection() {
323         return null;
324     }
325 
326     private void init(String name) {
327 
328         if (name == null || name.trim().length() == 0) {
329             throw new IllegalArgumentException("invalid empty name");
330         }
331 
332         ArrayList<CredOwner> pList = new ArrayList<>();
333         StringTokenizer tokenizer = new StringTokenizer(name, " ", true);
334         String principalClass = null;
335         String principalName = null;
336 
337         if (testing)
338             System.out.println("whole name = " + name);
339 
340         // get the Credential Class
341         credentialClass = tokenizer.nextToken();
342         if (testing)
343             System.out.println("Credential Class = " + credentialClass);
344 
345         if (tokenizer.hasMoreTokens() == false) {
346             MessageFormat form = new MessageFormat(ResourcesMgr.getString
347                 ("permission.name.name.syntax.invalid."));
348             Object[] source = {name};
349             throw new IllegalArgumentException
350                 (form.format(source) + ResourcesMgr.getString
351                         ("Credential.Class.not.followed.by.a.Principal.Class.and.Name"));
352         }
353 
354         while (tokenizer.hasMoreTokens()) {
355 
356             // skip delimiter
357             tokenizer.nextToken();
358 
359             // get the Principal Class
360             principalClass = tokenizer.nextToken();
361             if (testing)
362                 System.out.println("    Principal Class = " + principalClass);
363 
364             if (tokenizer.hasMoreTokens() == false) {
365                 MessageFormat form = new MessageFormat(ResourcesMgr.getString
366                         ("permission.name.name.syntax.invalid."));
367                 Object[] source = {name};
368                 throw new IllegalArgumentException
369                         (form.format(source) + ResourcesMgr.getString
370                         ("Principal.Class.not.followed.by.a.Principal.Name"));
371             }
372 
373             // skip delimiter
374             tokenizer.nextToken();
375 
376             // get the Principal Name
377             principalName = tokenizer.nextToken();
378 
379             if (!principalName.startsWith("\"")) {
380                 MessageFormat form = new MessageFormat(ResourcesMgr.getString
381                         ("permission.name.name.syntax.invalid."));
382                 Object[] source = {name};
383                 throw new IllegalArgumentException
384                         (form.format(source) + ResourcesMgr.getString
385                         ("Principal.Name.must.be.surrounded.by.quotes"));
386             }
387 
388             if (!principalName.endsWith("\"")) {
389 
390                 // we have a name with spaces in it --
391                 // keep parsing until we find the end quote,
392                 // and keep the spaces in the name
393 
394                 while (tokenizer.hasMoreTokens()) {
395                     principalName = principalName + tokenizer.nextToken();
396                     if (principalName.endsWith("\""))
397                         break;
398                 }
399 
400                 if (!principalName.endsWith("\"")) {
401                     MessageFormat form = new MessageFormat
402                         (ResourcesMgr.getString
403                         ("permission.name.name.syntax.invalid."));
404                     Object[] source = {name};
405                     throw new IllegalArgumentException
406                         (form.format(source) + ResourcesMgr.getString
407                                 ("Principal.Name.missing.end.quote"));
408                 }
409             }
410 
411             if (testing)
412                 System.out.println("\tprincipalName = '" + principalName + "'");
413 
414             principalName = principalName.substring
415                                         (1, principalName.length() - 1);
416 
417             if (principalClass.equals("*") &&
418                 !principalName.equals("*")) {
419                     throw new IllegalArgumentException(ResourcesMgr.getString
420                         ("PrivateCredentialPermission.Principal.Class.can.not.be.a.wildcard.value.if.Principal.Name.is.not.a.wildcard.value"));
421             }
422 
423             if (testing)
424                 System.out.println("\tprincipalName = '" + principalName + "'");
425 
426             pList.add(new CredOwner(principalClass, principalName));
427         }
428 
429         this.credOwners = new CredOwner[pList.size()];
430         pList.toArray(this.credOwners);
431     }
432 
433     private boolean impliesCredentialClass(String thisC, String thatC) {
434 
435         // this should never happen
436         if (thisC == null || thatC == null)
437             return false;
438 
439         if (testing)
440             System.out.println("credential class comparison: " +
441                                 thisC + "/" + thatC);
442 
443         if (thisC.equals("*"))
444             return true;
445 
446         /**
447          * XXX let's not enable this for now --
448          *      if people want it, we'll enable it later
449          */
450         /*
451         if (thisC.endsWith("*")) {
452             String cClass = thisC.substring(0, thisC.length() - 2);
453             return thatC.startsWith(cClass);
454         }
455         */
456 
457         return thisC.equals(thatC);
458     }
459 
460     private boolean impliesPrincipalSet(CredOwner[] thisP, CredOwner[] thatP) {
461 
462         // this should never happen
463         if (thisP == null || thatP == null)
464             return false;
465 
466         if (thatP.length == 0)
467             return true;
468 
469         if (thisP.length == 0)
470             return false;
471 
472         for (int i = 0; i < thisP.length; i++) {
473             boolean foundMatch = false;
474             for (int j = 0; j < thatP.length; j++) {
475                 if (thisP[i].implies(thatP[j])) {
476                     foundMatch = true;
477                     break;
478                 }
479             }
480             if (!foundMatch) {
481                 return false;
482             }
483         }
484         return true;
485     }
486 
487     /**
488      * Reads this object from a stream (i.e., deserializes it)
489      */
490     private void readObject(java.io.ObjectInputStream s) throws
491                                         java.io.IOException,
492                                         ClassNotFoundException {
493 
494         s.defaultReadObject();
495 
496         // perform new initialization from the permission name
497 
498         if (getName().indexOf(" ") == -1 && getName().indexOf("\"") == -1) {
499 
500             // name only has a credential class specified
501             credentialClass = getName();
502             credOwners = EMPTY_PRINCIPALS;
503 
504         } else {
505 
506             // perform regular initialization
507             init(getName());
508         }
509     }
510 
511     /**
512      * @serial include
513      */
514     static class CredOwner implements java.io.Serializable {
515 
516         private static final long serialVersionUID = -5607449830436408266L;
517 
518         /**
519          * @serial
520          */
521         String principalClass;
522         /**
523          * @serial
524          */
525         String principalName;
526 
527         CredOwner(String principalClass, String principalName) {
528             this.principalClass = principalClass;
529             this.principalName = principalName;
530         }
531 
532         public boolean implies(Object obj) {
533             if (obj == null || !(obj instanceof CredOwner))
534                 return false;
535 
536             CredOwner that = (CredOwner)obj;
537 
538             if (principalClass.equals("*") ||
539                 principalClass.equals(that.principalClass)) {
540 
541                 if (principalName.equals("*") ||
542                     principalName.equals(that.principalName)) {
543                     return true;
544                 }
545             }
546 
547             /**
548              * XXX no code yet to support a.b.*
549              */
550 
551             return false;
552         }
553 
554         public String toString() {
555             MessageFormat form = new MessageFormat(ResourcesMgr.getString
556                 ("CredOwner.Principal.Class.class.Principal.Name.name"));
557             Object[] source = {principalClass, principalName};
558             return (form.format(source));
559         }
560     }
561 }